1 /**
2 *   Public imports free, malloc, realloc, memcpy, memcmp, memset.
3 *   - toHeap!T executes malloc + memcpy
4 *   - safeFree executes free and sets the reference to null
5 *   - alloc!T is a malloc with the type size
6 *   - allocSlice!T will return a heap allocated slice 
7 *
8 */
9 module hip.util.memory;
10 
11 public import core.stdc.stdlib;
12 public import core.stdc.string:memcpy, memcmp, memset;
13 import hip.util.reflection;
14 
15 version(WebAssembly) version = CustomRuntime;
16 version(PSVita) version = CustomRuntime;
17 version(CustomRuntimeTest) version = CustomRuntime;
18 
19 version(CustomRuntime)
20     static import rt.hooks;
21 
22 @nogc:
23 void setZeroMemory(T)(ref T variable)
24 {
25     memset(&variable, 0, T.sizeof);
26 }
27 
28 
29 T* alloc(T)(size_t count = 1){return cast(T*)core.stdc.stdlib.malloc(T.sizeof*count);}
30 T[] allocSlice(T)(size_t count){return alloc!T(count)[0..count];}
31 
32 void* toHeap(T)(in T data) if(isReference!T)
33 {
34     version(CustomRuntime)
35     {
36         void* m = cast(void*)data; //WASM don't need to allocate as it is not ever deleted.
37     }
38     else
39     {
40         import core.memory;
41         void* m = cast(void*)data;
42         GC.addRoot(cast(void*)data);
43     }
44     return m;
45 }
46 
47 void* toHeap(T)(T data) if(!isReference!T)
48 {
49     void* m = alloc!T;
50     memcpy(m, &data, T.sizeof);
51     return m;
52 }
53 
54 void[] toHeapSlice(T)(T data) if(!is(T == void[]))
55 {
56     return toHeap(data)[0..T.sizeof];
57 }
58 
59 
60 void freeGCMemory(void* data)
61 {
62     assert(data !is null, "Tried to free null data.");
63     version(CustomRuntime){rt.hooks.free(cast(ubyte*)data);}
64     else
65     {
66         import core.memory;
67         GC.removeRoot(data);
68         GC.free(data);
69     }
70 }
71 
72 void freeGCMemory(ref void* data) //Remove ref.
73 {
74     assert(data !is null, "Tried to free null data.");
75     version(CustomRuntime){rt.hooks.free(cast(ubyte*)data);}
76     else
77     {
78         import core.memory;
79         GC.removeRoot(data);
80         GC.free(data);
81     }
82     data = null;
83 }
84 
85 void freeGCMemory(ref void[] data)
86 {
87     assert(data.length, "Tried to free null data.");
88     freeGCMemory(data.ptr);
89     data = null;
90 }
91 
92 void freeGCMemory(T)(ref T[] data)
93 {
94     freeGCMemory(cast(void*)data.ptr);
95     data = null;
96 }
97 
98 void safeFree(T)(ref T data) if(isReference!T)
99 {
100     version(CustomRuntime)
101     {
102         free(cast(ubyte*)data);
103     }
104     else
105     {
106         import core.memory;
107         GC.removeRoot(cast(void*)data);
108         GC.free(cast(void*)data);
109     }
110     data = null;
111 }
112 
113 void safeFree(ref void* data)
114 {
115     if(data != null)
116         free(data);
117     data = null;
118 }
119 void safeFree(ref void[] data)
120 {
121     if(data.ptr !is null)
122         free(data.ptr);
123     data = [];
124 }
125 
126 class Pool(T) if(is(T == class) || is(T == interface))
127 {
128     private
129     {
130         T[] objects;
131         int deadCount, maxPoolSize = -1;
132         pragma(inline, true) T getFirstDead(){return objects[getActiveCount];}
133 
134         struct TInterface
135         {
136             pragma(inline, true)
137             {
138                 static void deinitialize(T obj){obj.deinitialize();}
139                 static void initialize(T obj){obj.initialize();}
140             }
141         }
142     }
143 
144     /** 
145      * 
146      * Params:
147      *   maxPoolSize = -1 means that Pool will never return null. It may still give array out of bounds if too many objects are created.
148      */
149     this(int maxPoolSize = -1)
150     {
151         this.maxPoolSize = maxPoolSize;
152     }
153     int getActiveCount() => cast(int)objects.length - deadCount;
154     
155     T get(Args...)(Args a)
156     {
157         T ret;
158         if(deadCount > 0)
159         {
160             ret = getFirstDead();
161             deadCount--;
162         }
163         else
164         {
165             if(objects.length + 1 > maxPoolSize) return null;
166             int activeCount = getActiveCount();
167             objects.length++;
168             if(deadCount)
169                 objects[$-1] = objects[activeCount];
170             objects[activeCount] = ret = new T(a);
171         }
172         TInterface.initialize(ret);
173         return ret;
174     }
175 
176     int opApply(scope int delegate(ref T) dg)
177     {
178         int result = 0;
179         
180         foreach (i; 0..getActiveCount)
181         {
182             result = dg(objects[i]);
183             if (result)
184                 break;
185         }
186         return result;
187     }
188 
189     void kill(T instance)
190     {
191         TInterface.deinitialize(instance);
192         deadCount++;
193     }
194     void clear()
195     {
196         foreach(obj; this) TInterface.deinitialize(obj);
197         deadCount = cast(int)objects.length;
198     }
199 }